package kr.ac.konkuk.db.quadtree.node;

import java.util.Collection;
import java.util.Collections;
import java.util.Set;

import kr.ac.konkuk.db.quadtree.RowKey;
import kr.ac.konkuk.db.quadtree.callback.GeometryProvider;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;

public class Node extends NodeBase {
	protected NodeBase subnodes[] = new NodeBase[4];
	
	  /**
	   * subquads are numbered as follows:
	   * <pre>
	   *  2 | 3
	   *  --+--
	   *  0 | 1
	   * </pre>
	   */
	public static int getSubnodeIndex(Envelope env, Coordinate centre) {
		int subnodeIndex = -1;
		if (env.getMinX() >= centre.x) {
			if (env.getMinY() >= centre.y)
				subnodeIndex = RIGHT_TOP_INDEX;
			if (env.getMaxY() <= centre.y)
				subnodeIndex = RIGHT_BOTTOM_INDEX;
		}
		if (env.getMaxX() <= centre.x) {
			if (env.getMinY() >= centre.y)
				subnodeIndex = LEFT_TOP_INDEX;
			if (env.getMaxY() <= centre.y)
				subnodeIndex = LEFT_BOTTOM_INDEX;
		}
		return subnodeIndex;
	}
	
	private static Envelope []quads(Envelope env) {
		Coordinate centre = env.centre();
		Envelope quads[] = new Envelope[4];
		quads[LEFT_BOTTOM_INDEX] = new Envelope(env.getMinX(), centre.x, env.getMinY(), centre.y);
		quads[LEFT_TOP_INDEX] = new Envelope(env.getMinX(), centre.x, centre.y, env.getMaxY());
		quads[RIGHT_BOTTOM_INDEX] = new Envelope(centre.x, env.getMaxX(), env.getMinY(), centre.y);
		quads[RIGHT_TOP_INDEX] = new Envelope(centre.x, env.getMaxX(), centre.y, env.getMaxY());
		
		return quads;
	} 

	public Node(String cf, Envelope env, GeometryProvider provider) {
		super(cf, env, provider);
		
		Envelope []quads = quads(env);
		for (int i = 0 ; i < 4 ; ++i) {
			subnodes[i] = new LeafNode(cf + i, quads[i], this.geometryProvider);
		}
	}
	
	public void put(RowKey rowKey, Coordinate coord, Set<String> cfSet) {
		for (int i = 0 ; i < subnodes.length ; ++i) {
			NodeBase child = subnodes[i];
			Envelope childEnv = child.env();
			if (childEnv.contains(coord)) {
				child.put(rowKey, coord, cfSet);
			
				if (child instanceof LeafNode) {
					LeafNode leafChild = (LeafNode)child;
					if (leafChild.isSplit()) {
						Node newNode = new Node(cf + i, leafChild.env(), this.geometryProvider);
						leafChild.split(newNode);
						subnodes[i] = newNode;
					}
				}
			}
		}
	}
	
	public void search(Envelope searchEnv, NodeVisitor visitor) {
		for (int i = 0 ; i < 4 ; ++i) {
			Envelope nodeEnv = subnodes[i].env();
			if (nodeEnv.intersects(searchEnv)) {
				subnodes[i].search(searchEnv, visitor);
			}
		}
	}
	
	public Collection<Coordinate> remove(RowKey rowKey, Coordinate coord) {
		for (int i = 0 ; i < 4 ; ++i) {
			Envelope nodeEnv = subnodes[i].env();
			if (nodeEnv.contains(coord)) {
				return subnodes[i].remove(rowKey, coord);
			}
		}
		
		return Collections.emptySet();
	}

	public String toString(int level) {
		StringBuilder sb = new StringBuilder(1024);
		sb.append(super.toString(level))
			.append("@Node")
			.append('\n');
		for (int i = 0 ; i < 4 ; ++i) {
			sb.append(subnodes[i].toString(level + 1));
			if (i < 3) {
				sb.append('\n');
			}
		}
		return sb.toString();
	}
	
}
